// AgreementMatrix.cs
//
// Copyright (c) 2007 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.Collections.ObjectModel;
using System.Linq;

using Supremacy.Entities;
using Supremacy.Game;
using Supremacy.IO.Serialization;

namespace Supremacy.Diplomacy
{
    [Serializable]
    public class AgreementMatrix : IOwnedDataSerializableAndRecreatable
    {
        private CivilizationPairedMap<IAgreement[]> _map;

        public AgreementMatrix()
        {
            Initialize();
        }

        public void AddAgreement(IAgreement agreement)
        {
            if (agreement == null)
                throw new ArgumentNullException("agreement");

            int firstCivId = agreement.SenderID;
            int secondCivId = agreement.RecipientID;

            ReorderCivIds(ref firstCivId, ref secondCivId);

            var activeAgreements = this[firstCivId, secondCivId];

            _map[firstCivId, secondCivId] =
                activeAgreements.Concat(Enumerable.DefaultIfEmpty(Enumerable.Empty<IAgreement>(), agreement)).ToArray();
        }

        public bool IsAgreementActive(Civilization firstCiv, Civilization secondCiv, ClauseType clauseType)
        {
            if (firstCiv == null)
                throw new ArgumentNullException("firstCiv");
            if (secondCiv == null)
                throw new ArgumentNullException("secondCiv");
            return IsAgreementActive(firstCiv.CivID, secondCiv.CivID, clauseType);
        }

        public bool IsAgreementActive(int firstCivId, int secondCivId, ClauseType clauseType)
        {
            return this[firstCivId, secondCivId].Any(a => a.Proposal.Clauses.Any(c => c.ClauseType == clauseType));
        }

        public void Remove(IAgreement agreement)
        {
            if (agreement == null)
                return;
            Remove(agreement.SenderID, agreement.RecipientID, a => agreement.Equals(a));
        }

        public void Remove(Civilization firstCiv, Civilization secondCiv, Func<IAgreement, bool> predicate)
        {
            if (firstCiv == null)
                throw new ArgumentNullException("firstCiv");
            if (secondCiv == null)
                throw new ArgumentNullException("secondCiv");
            Remove(firstCiv.CivID, secondCiv.CivID, predicate);
        }

        public void Remove(int firstCivId, int secondCivId, Func<IAgreement, bool> predicate)
        {
            if (predicate == null)
                throw new ArgumentNullException("predicate");

            ReorderCivIds(ref firstCivId, ref secondCivId);

            var agreements = _map[firstCivId, secondCivId];
            _map[firstCivId, secondCivId] = agreements.Except(agreements.Where(predicate)).ToArray();
        }

        public ReadOnlyCollection<IAgreement> this[Civilization firstCiv, Civilization secondCiv]
        {
            get
            {
                if (firstCiv == null)
                    throw new ArgumentNullException("firstCiv");
                if (secondCiv == null)
                    throw new ArgumentNullException("secondCiv");
                return this[firstCiv.CivID, secondCiv.CivID];
            }
        }

        public ReadOnlyCollection<IAgreement> this[int firstCivId, int secondCivId]
        {
            get
            {
                bool result;
                IAgreement[] value;
                ReorderCivIds(ref firstCivId, ref secondCivId);
                if (!(result = _map.TryGetValue(firstCivId, secondCivId, out value)))
                {
                    value = new IAgreement[0];
                }
                return Array.AsReadOnly(value);
            }
        }

        protected void ReorderCivIds(ref int firstCivId, ref int secondCivId)
        {
            if (secondCivId < firstCivId)
            {
                int temp = firstCivId;
                firstCivId = secondCivId;
                secondCivId = temp;
            }
        }

        #region IOwnedDataSerializable Members
        public void SerializeOwnedData(SerializationWriter writer, object context)
        {
            _map.SerializeOwnedData(writer, context);
        }

        public void DeserializeOwnedData(SerializationReader reader, object context)
        {
            Initialize();
            _map.DeserializeOwnedData(reader, context);
        }

        private void Initialize()
        {
            _map = new CivilizationPairedMap<IAgreement[]>();
        }
        #endregion
    }
}